{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "https://langchain-ai.github.io/langgraph/how-tos/human_in_the_loop/time-travel/#build-the-agent"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "e366b30b9a12a5b3"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.core.display import Image\n",
    "import os\n",
    "from dotenv import load_dotenv\n",
    "# Set up the tool\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.graph import MessagesState, START\n",
    "from langgraph.prebuilt import ToolNode\n",
    "from langgraph.graph import END, StateGraph\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "\n",
    "@tool\n",
    "def play_song_on_spotify(song: str):\n",
    "    \"\"\"Play a song on Spotify\"\"\"\n",
    "    # Call the spotify API ...\n",
    "    return f\"Successfully played {song} on Spotify!\"\n",
    "\n",
    "\n",
    "@tool\n",
    "def play_song_on_apple(song: str):\n",
    "    \"\"\"Play a song on Apple Music\"\"\"\n",
    "    # Call the apple music API ...\n",
    "    return f\"Successfully played {song} on Apple Music!\"\n",
    "\n",
    "\n",
    "tools = [play_song_on_apple, play_song_on_spotify]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "load_dotenv()\n",
    "model = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    temperature=0, streaming=True,\n",
    ").bind_tools(tools)\n",
    "\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return \"end\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"continue\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "# Define a new graph\n",
    "workflow = StateGraph(MessagesState)\n",
    "\n",
    "# Define the two nodes we will cycle between\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "# Set the entrypoint as `agent`\n",
    "# This means that this node is the first one called\n",
    "workflow.add_edge(START, \"agent\")\n",
    "\n",
    "# We now add a conditional edge\n",
    "workflow.add_conditional_edges(\n",
    "    # First, we define the start node. We use `agent`.\n",
    "    # This means these are the edges taken after the `agent` node is called.\n",
    "    \"agent\",\n",
    "    # Next, we pass in the function that will determine which node is called next.\n",
    "    should_continue,\n",
    "    # Finally we pass in a mapping.\n",
    "    # The keys are strings, and the values are other nodes.\n",
    "    # END is a special node marking that the graph should finish.\n",
    "    # What will happen is we will call `should_continue`, and then the output of that\n",
    "    # will be matched against the keys in this mapping.\n",
    "    # Based on which one it matches, that node will then be called.\n",
    "    {\n",
    "        # If `tools`, then we call the tool node.\n",
    "        \"continue\": \"action\",\n",
    "        # Otherwise we finish.\n",
    "        \"end\": END,\n",
    "    },\n",
    ")\n",
    "\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "\n",
    "# Set up memory\n",
    "memory = MemorySaver()\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "\n",
    "# We add in `interrupt_before=[\"action\"]`\n",
    "# This will add a breakpoint before the `action` node is called\n",
    "graph = workflow.compile(checkpointer=memory)\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:23:46.551420Z",
     "start_time": "2024-11-05T02:23:42.779606Z"
    }
   },
   "id": "64a4dfb147fa2396",
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001B[1m Human Message \u001B[0m=================================\n",
      "\n",
      "Can you play Taylor Swift's most popular song?\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "Tool Calls:\n",
      "  play_song_on_spotify (call_babc0ee261ea402da1537f)\n",
      " Call ID: call_babc0ee261ea402da1537f\n",
      "  Args:\n",
      "    song: Taylor Swift - Shake It Off\n",
      "=================================\u001B[1m Tool Message \u001B[0m=================================\n",
      "Name: play_song_on_spotify\n",
      "\n",
      "Successfully played Taylor Swift - Shake It Off on Spotify!\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "\n",
      "Successfully played Taylor Swift's most popular song, \"Shake It Off,\" on Spotify! Enjoy the music!\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "input_message = HumanMessage(content=\"Can you play Taylor Swift's most popular song?\")\n",
    "for event in graph.stream({\"messages\": [input_message]}, config, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:24:01.874066Z",
     "start_time": "2024-11-05T02:23:58.357916Z"
    }
   },
   "id": "d7e7f6a56362d7f7",
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Checking history\n",
    "让我们从头到尾浏览这个线程的历史"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "5abdb493f7db08ae"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "[HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe'),\n AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}]),\n ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='a95c1094-cc8b-4946-b194-dd7c1db00e66', tool_call_id='call_babc0ee261ea402da1537f'),\n AIMessage(content='Successfully played Taylor Swift\\'s most popular song, \"Shake It Off,\" on Spotify! Enjoy the music!', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'qwen-max'}, id='run-b962e13a-bc72-44b9-97fa-85e43973dbd3-0')]"
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "graph.get_state(config).values[\"messages\"]\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:24:59.912781Z",
     "start_time": "2024-11-05T02:24:59.898298Z"
    }
   },
   "id": "4eca4ebb7a038dd1",
   "execution_count": 3
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe'), AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}]), ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='a95c1094-cc8b-4946-b194-dd7c1db00e66', tool_call_id='call_babc0ee261ea402da1537f'), AIMessage(content='Successfully played Taylor Swift\\'s most popular song, \"Shake It Off,\" on Spotify! Enjoy the music!', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'qwen-max'}, id='run-b962e13a-bc72-44b9-97fa-85e43973dbd3-0')]}, next=(), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-565a-6a8c-8003-d5994e18efdd'}}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [AIMessage(content='Successfully played Taylor Swift\\'s most popular song, \"Shake It Off,\" on Spotify! Enjoy the music!', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'qwen-max'}, id='run-b962e13a-bc72-44b9-97fa-85e43973dbd3-0')]}}, 'step': 3, 'parents': {}}, created_at='2024-11-05T02:24:01.868009+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-48ea-67ee-8002-923d18534e73'}}, tasks=())\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe'), AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}]), ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='a95c1094-cc8b-4946-b194-dd7c1db00e66', tool_call_id='call_babc0ee261ea402da1537f')]}, next=('agent',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-48ea-67ee-8002-923d18534e73'}}, metadata={'source': 'loop', 'writes': {'action': {'messages': [ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='a95c1094-cc8b-4946-b194-dd7c1db00e66', tool_call_id='call_babc0ee261ea402da1537f')]}}, 'step': 2, 'parents': {}}, created_at='2024-11-05T02:24:00.458929+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-48d6-65c8-8001-553b38c6ebc4'}}, tasks=(PregelTask(id='ad066128-f93f-d79c-1d67-355a76b2ed5e', name='agent', path=('__pregel_pull', 'agent'), error=None, interrupts=(), state=None, result={'messages': [AIMessage(content='Successfully played Taylor Swift\\'s most popular song, \"Shake It Off,\" on Spotify! Enjoy the music!', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'qwen-max'}, id='run-b962e13a-bc72-44b9-97fa-85e43973dbd3-0')]}),))\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe'), AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}])]}, next=('action',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-48d6-65c8-8001-553b38c6ebc4'}}, metadata={'source': 'loop', 'writes': {'agent': {'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}])]}}, 'step': 1, 'parents': {}}, created_at='2024-11-05T02:24:00.450651+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-3501-63b8-8000-b34a7d6bb106'}}, tasks=(PregelTask(id='7114e6f8-5aaf-aff7-bf5e-2c6a3f908d55', name='action', path=('__pregel_pull', 'action'), error=None, interrupts=(), state=None, result={'messages': [ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='a95c1094-cc8b-4946-b194-dd7c1db00e66', tool_call_id='call_babc0ee261ea402da1537f')]}),))\n",
      "--\n",
      "StateSnapshot(values={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe')]}, next=('agent',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-3501-63b8-8000-b34a7d6bb106'}}, metadata={'source': 'loop', 'writes': None, 'step': 0, 'parents': {}}, created_at='2024-11-05T02:23:58.371090+00:00', parent_config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-34f8-6fd8-bfff-49a35a12531a'}}, tasks=(PregelTask(id='73ec0fb1-788b-f3c4-caa3-8e8cf2ad6102', name='agent', path=('__pregel_pull', 'agent'), error=None, interrupts=(), state=None, result={'messages': [AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}])]}),))\n",
      "--\n",
      "StateSnapshot(values={'messages': []}, next=('__start__',), config={'configurable': {'thread_id': '1', 'checkpoint_ns': '', 'checkpoint_id': '1ef9b1d0-34f8-6fd8-bfff-49a35a12531a'}}, metadata={'source': 'input', 'writes': {'__start__': {'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={})]}}, 'step': -1, 'parents': {}}, created_at='2024-11-05T02:23:58.367716+00:00', parent_config=None, tasks=(PregelTask(id='f6af6c76-e5ec-1de5-2b7c-fdad8771863a', name='__start__', path=('__pregel_pull', '__start__'), error=None, interrupts=(), state=None, result={'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={})]}),))\n",
      "--\n"
     ]
    }
   ],
   "source": [
    "all_states = []\n",
    "for state in graph.get_state_history(config):\n",
    "    print(state)\n",
    "    all_states.append(state)\n",
    "    print(\"--\")"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:25:29.042473Z",
     "start_time": "2024-11-05T02:25:29.017829Z"
    }
   },
   "id": "36305eebdd2e23d9",
   "execution_count": 4
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Replay a state\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "80df0cddbf34188a"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe'),\n  AIMessage(content='', additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_babc0ee261ea402da1537f', 'function': {'arguments': '{\"song\": \"Taylor Swift - Shake It Off\"}', 'name': 'play_song_on_spotify'}, 'type': 'function'}]}, response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0', tool_calls=[{'name': 'play_song_on_spotify', 'args': {'song': 'Taylor Swift - Shake It Off'}, 'id': 'call_babc0ee261ea402da1537f', 'type': 'tool_call'}])]}"
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_replay = all_states[2]\n",
    "\n",
    "to_replay.values"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:26:45.728828Z",
     "start_time": "2024-11-05T02:26:45.722721Z"
    }
   },
   "id": "ecbf694ea5122883",
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "('action',)"
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_replay.next\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:26:58.023132Z",
     "start_time": "2024-11-05T02:26:58.017533Z"
    }
   },
   "id": "6af108ff714cf37a",
   "execution_count": 6
  },
  {
   "cell_type": "markdown",
   "source": [
    "要从这里重播，我们只需将它的配置传回代理。请注意，它将从上次中断的地方开始，再次进行工具调用。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "d3f53f988777150a"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='c79eb61b-73d3-4e5b-be3a-f08373da559b', tool_call_id='call_babc0ee261ea402da1537f')]}\n",
      "{'messages': [AIMessage(content='I\\'ve played Taylor Swift\\'s very popular song \"Shake It Off\" on Spotify for you! Enjoy the music.', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'qwen-max'}, id='run-f874e45f-b8c9-4955-8c9e-1dd5f3c0fe6a-0')]}\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, to_replay.config):\n",
    "    for v in event.values():\n",
    "        print(v)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:28:15.808873Z",
     "start_time": "2024-11-05T02:28:13.662381Z"
    }
   },
   "id": "3f1abc8980ad0166",
   "execution_count": 7
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Branch off a past state\n",
    "利用 LangGraph 的检查点，你可以做的事情不止是从过去的状态重新播放。你可以从先前的位置分支出去，让代理探索替代轨迹，或者让用户“版本控制”工作流程中的更改。\n",
    "\n",
    "让我们演示一下如何编辑特定时间点的状态。让我们将状态更新为在 Spotify 上播放歌曲，而不是在 Apple 上播放："
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "a9f1dc96a7c54a7d"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "# Let's now get the last message in the state\n",
    "# This is the one with the tool calls that we want to update\n",
    "last_message = to_replay.values[\"messages\"][-1]\n",
    "\n",
    "\n",
    "# Let's now update the tool we are calling\n",
    "last_message.tool_calls[0][\"name\"] = \"play_song_on_spotify\"\n",
    "\n",
    "branch_config = graph.update_state(\n",
    "    to_replay.config,\n",
    "    {\"messages\": [last_message]},\n",
    ")\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:30:02.954200Z",
     "start_time": "2024-11-05T02:30:02.931016Z"
    }
   },
   "id": "d635aec218688da4",
   "execution_count": 8
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'messages': [ToolMessage(content='Successfully played Taylor Swift - Shake It Off on Spotify!', name='play_song_on_spotify', id='0d508e32-6917-4d77-8d95-5ac3d917ae48', tool_call_id='call_babc0ee261ea402da1537f')]}\n",
      "{'messages': [AIMessage(content='I\\'ve started playing Taylor Swift\\'s very popular song \"Shake It Off\" on Spotify for you! Enjoy the music.', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'qwen-max'}, id='run-865b33c3-9f00-49d1-b843-36c6207225b9-0')]}\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, branch_config):\n",
    "    for v in event.values():\n",
    "        print(v)\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:30:20.335399Z",
     "start_time": "2024-11-05T02:30:17.890373Z"
    }
   },
   "id": "3180935c1c78b25a",
   "execution_count": 9
  },
  {
   "cell_type": "markdown",
   "source": [
    "Alternatively, we could update the state to not even call a tool!\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "c801701578ac624c"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "{'messages': [HumanMessage(content=\"Can you play Taylor Swift's most popular song?\", additional_kwargs={}, response_metadata={}, id='8bb11ec4-9188-4dc9-8167-6ec981f53bbe'),\n  AIMessage(content=\"It's quiet hours so I can't play any music right now!\", additional_kwargs={}, response_metadata={}, id='run-347434a4-5af8-4e16-90cb-5667e52a5ab3-0')]}"
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.messages import AIMessage\n",
    "\n",
    "# Let's now get the last message in the state\n",
    "# This is the one with the tool calls that we want to update\n",
    "last_message = to_replay.values[\"messages\"][-1]\n",
    "\n",
    "# Let's now get the ID for the last message, and create a new message with that ID.\n",
    "new_message = AIMessage(\n",
    "    content=\"It's quiet hours so I can't play any music right now!\", id=last_message.id\n",
    ")\n",
    "\n",
    "branch_config = graph.update_state(\n",
    "    to_replay.config,\n",
    "    {\"messages\": [new_message]},\n",
    ")\n",
    "branch_state = graph.get_state(branch_config)\n",
    "branch_state.values\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:31:45.462152Z",
     "start_time": "2024-11-05T02:31:45.447400Z"
    }
   },
   "id": "11aa4b01e999a1c",
   "execution_count": 11
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "()"
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "branch_state.next\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T02:31:50.352990Z",
     "start_time": "2024-11-05T02:31:50.348172Z"
    }
   },
   "id": "e78b56c7dcc088ed",
   "execution_count": 12
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
